#include "swagme.h"
#include "Game.h"
#include "Display.h"
#include "Player.h"
#include "Scene.h"

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;

// 3 minutes for each screen
#define TIME_LEFT (3*60)

Game::Game() : 
    mTimeLeftInSec(TIME_LEFT), 
    mStartTimeInMs(SDL_GetTicks()), 
    mpDisplay(new Display()),
    mpScene(new Scene("resources/graphics/background1.png", "resources/graphics/map2.png")),
    mpPlayer(new Player(PLAYER_POS_MIN_X,PLAYER_POS_MIN_Y)),
    mpPlayerSprite(NULL)
{
  InitConfig("resources/wad.xml");

  if(!mpDisplay) {
    cerr << "Error!  Could not initialize Display" << endl;
    exit(4);
  }
  cout << "Game and Display initialized." << endl;
  if(!mpScene) { 
    cerr << "Error!  Could not initialize scene" << endl;
    exit(5);
  }
  InitScene();
}

Game::~Game()
{
  // TO DO: properly clean up sprite objects
  if(mpPlayerSprite) {
    delete mpPlayerSprite;
    mpPlayerSprite = NULL;
  }

  if(mpScene) {
    delete mpScene;
    mpScene = NULL;
  }

  if(mpDisplay) {
    delete mpDisplay;
    mpDisplay = NULL;
  }
  cout << "Game and Display destroyed." << endl;
}

void Game::InitScene()
{
  // uses scene member variable
  if (!mpScene->Background) mpScene->Background = IMG_Load(mpScene->BackgroundImageFile);
  if (!mpScene->Background) {
    printf("Unable to load background image: %s\n", SDL_GetError());
    exit(1);
  }
  if (!mpScene->Map) mpScene->Map = IMG_Load(mpScene->MapImageFile);
  if (!mpScene->Map) {
    printf("Unable to load map image: %s\n", SDL_GetError());
    exit(1);
  }

  if (!mpPlayerSprite) mpPlayerSprite = new Sprite("resources/graphics/fatdude1.png");
}

// primarily what this is does is update the time left
// and display to the status area
void Game::Update(Uint8* keystate)
{
  if(IsGameOver()) { return; }

  if(mTimeLeftInSec > 0) {
    int oldTime = mTimeLeftInSec;
    mTimeLeftInSec = TIME_LEFT - (SDL_GetTicks() - mStartTimeInMs)/1000;
    if(mTimeLeftInSec != oldTime) {
//    cout << "Time Left (s) = " << mTimeLeftInSec << " seconds." << endl;
    }
  }

  static int frame=1;
  
  // keyboard controls:
  // directions:
  //  w,a,d,s or up, left, down, right arrows or num pad equiv
  sm_direction playerMoveDirection = sm_dir_none;
  if (keystate[SDLK_w] || keystate[SDLK_UP] || keystate[SDLK_KP8]) {
    playerMoveDirection = sm_dir_up;
  } 
  if (keystate[SDLK_a] || keystate[SDLK_LEFT] || keystate[SDLK_KP4]) {
    playerMoveDirection = sm_dir_left;
  } 
  if (keystate[SDLK_s] || keystate[SDLK_DOWN] || keystate[SDLK_KP2]) {
    playerMoveDirection = sm_dir_down;
  } 
  if (keystate[SDLK_d] || keystate[SDLK_RIGHT] || keystate[SDLK_KP6]) {
    playerMoveDirection = sm_dir_right;
  }

  if (playerMoveDirection != sm_dir_none) {
//    cout << "try to move " << playerMoveDirection << endl;
  }

  // save the previous position of the player
  mpPlayer->prevX = mpPlayer->posX;
  mpPlayer->prevY = mpPlayer->posY;
  mpPlayer->prevFacing = mpPlayer->Facing;

  if(playerMoveDirection == sm_dir_up) {
    mpPlayer->posY -= mpPlayer->speed;
  }
  if(playerMoveDirection == sm_dir_left) {
    mpPlayer->posX -= mpPlayer->speed;
  }
  if(playerMoveDirection == sm_dir_down) {
    mpPlayer->posY += mpPlayer->speed;
  }
  if(playerMoveDirection == sm_dir_right) {
    mpPlayer->posX += mpPlayer->speed;
  }

  // bounds check
  if (mpPlayer->posX > PLAYER_POS_MAX_X) {
    mpPlayer->posX = PLAYER_POS_MAX_X;
  }
  else if (mpPlayer->posX < PLAYER_POS_MIN_X) {
    mpPlayer->posX = PLAYER_POS_MIN_X;
  }
  if (mpPlayer->posY > PLAYER_POS_MAX_Y) {
    mpPlayer->posY = PLAYER_POS_MAX_Y;
  }
  else if (mpPlayer->posY < PLAYER_POS_MIN_Y) {
    mpPlayer->posY = PLAYER_POS_MIN_Y;
  }

  if ( (mpPlayer->prevX != mpPlayer->posX) || (mpPlayer->prevY != mpPlayer->posY) ) {
    frame++;
//    cout << "New player position (" << mpPlayer->posX << "," << mpPlayer->posY << ")" << endl;
  }
  if (mpPlayer->prevFacing != mpPlayer->Facing) {
    frame = 1;
  }

  mpDisplay->LockScreen();

  // commit to screen
  DrawScene();
  // update sprites
  mpPlayerSprite->Draw(playerMoveDirection, mpPlayer->speed, frame, mpDisplay->getScreen(),mpPlayer->posX+MAP_POS_X,mpPlayer->posY+MAP_POS_Y);

  // this doesn't do anything yet, but mSprites could contain animations in the scene and maybe NPCs
  for (int spriteIdx = 0; spriteIdx < mSprites.size(); spriteIdx++) {
    mSprites[spriteIdx].Blit(mpDisplay->getScreen(),mpPlayer->posX,mpPlayer->posY);
  }

  
  // TODO: only update when i need to
  DrawStatus();

  // flip the screen now that all rendering is complete 
  mpDisplay->UnlockScreen();
  mpDisplay->Flip();
}

void Game::DrawScene()
{
  // blit in layers,
  //  1. background
  //  2. map and status graphics
  //  3. sprites
//  SDL_BlitSurface(mpScene->Background, NULL, mpDisplay->getScreen(), NULL);

  // temporary background - two rectangles against black for now 
  SDL_Surface* scr = mpDisplay->getScreen();
  SDL_Rect rcMap = { MAP_POS_X, MAP_POS_Y, MAP_SIZE_X, MAP_SIZE_Y };
  SDL_Rect rcStatus = { STATUS_POS_X, STATUS_POS_Y, STATUS_SIZE_X, STATUS_SIZE_Y };
  SDL_FillRect(scr, NULL, SDL_MapRGB(scr->format, 0, 0, 0)); // black
  SDL_FillRect(scr, &rcStatus, SDL_MapRGB(scr->format, 192, 255, 192)); // lt green

//  SDL_BlitSurface(mpScene->Map, &mpScene->MapRect, mpDisplay->getScreen(), &mpScene->MapRect);
  SDL_BlitSurface(mpScene->Map, NULL, mpDisplay->getScreen(), &mpScene->MapRect);
}

void Game::DrawStatus()
{
  if(!mpDisplay || !mpPlayer) { return; }

  char szTimeStr[30];
  int time_x = STATUS_POS_X + 20; int time_y = STATUS_POS_Y + 50;
  SDL_Color time_color = {0,0,0,255}; // black
  if(mTimeLeftInSec > 0) {
    sprintf(szTimeStr, "Time Left: %d:%02d", mTimeLeftInSec/60, mTimeLeftInSec%60);
  }
  else {
    sprintf(szTimeStr, "Game over, chump!");
    time_color.r = 255; // red
  }

  char szPlayerStr[20];
  int player_x = STATUS_POS_X + 20; int player_y = STATUS_POS_Y + 100;
  sprintf(szPlayerStr, "(%d,%d)", mpPlayer->posX, mpPlayer->posY);

  // rendering calls go here
  mpDisplay->SetCurrentFont("resources/fonts/Vera.ttf", 14);
  mpDisplay->DrawText(szTimeStr, time_color, time_x, time_y);
  mpDisplay->DrawText(szPlayerStr, time_color, player_x, player_y);
}
